import { defineStore } from "pinia";
import { GRID, PROP, BUTTLET, TANK, ENEMY } from "@/utils/global";
import { usePlayerStore } from "./player";
import { useEnemyStore } from './enemy'
import { useExplosionStore } from './explosion'
import { useProtectionStore } from './protection'
import { useMapStore } from "./map";

// 概率模型 初始权重
const rates = {
  tank: 25,
  star: 30,
  grenade: 20,
  watch: 30,
  helmet: 30,
  shovel: 30,
  pistol: 15
}

export const usePropStore = defineStore('prop', {
  state: () => ({
    prop: new Map(),
    id: 0,
    rates,
    effect: new Map(),
  }),
  actions: {
    bornProp() {
      // 随机类型
      const keys = Object.keys(this.rates)
      const typeReduce = keys.reduce((a, b) => a + this.rates[b], 0)
      const seed = Math.random()
      let type = ''
      for (let i = 1; i <= keys.length; i++) {
        const k = keys[i - 1]
        if (seed < keys.slice(0, i).reduce((a, b) => a + this.rates[b], 0) / typeReduce) {
          type = k

          // 类型动态补偿机制
          // 补偿权重
          const weight = 6
          this.rates[k] -= weight
          keys.forEach(key => {
            if (key !== k) {
              this.rates[key] += (weight / (keys.length - 1))
            }
          })
          break
        }
      }

      /**
       * 随机坐标
       * 距离边界1*BLOCK范围内不出现
       * 不能和已经存在prop有位置重叠
       */
      let x = (Math.floor(Math.random() * 21) + 2) * GRID,
        y = (Math.floor(Math.random() * 21) + 2) * GRID,
        flag = true,
        s = 0
      
      while (flag) {
        for (const [_, v] of this.prop) {
          if (Math.abs(x - v.x) < PROP.size && Math.abs(y - v.y) < PROP.size) {
            x = (Math.floor(Math.random() * 21) + 2) * GRID
            y = (Math.floor(Math.random() * 21) + 2) * GRID
          } else {
            s++
            if (s >= this.prop.size) {
              flag = false
            }
          }
        }
      }

      const o = {
        id: this.id,
        type,
        x,
        y,
        visible: true,
        prog: 0,
        dur: 0,
      }
      this.prop.set(this.id, o)
      this.id++
    },
    initProp() {
      this.prop.clear()
      this.id = 0
      this.rates = rates
    },
    playProp(ft) {
      const playerStore = usePlayerStore()

      for (const [_, v] of this.prop) {
        let skip = false

        for (const [_, pv] of playerStore.players) {
          if (Math.abs(pv.x - v.x) < PROP.size && Math.abs(pv.y - v.y) < PROP.size) {
            /** 享受道具效果 */
            const enemyStore = useEnemyStore()
            const explosionStore = useExplosionStore()
            const protectionStore = useProtectionStore()
            pv.score += PROP.score

            switch (v.type) {
              case 'tank':
                pv.hp++
                break
              case 'star':
                pv.power = Math.min(pv.power + 1, BUTTLET.level.length - 1)
                break
              case 'grenade':
                for (const [_, nv] of enemyStore.enemies) {
                  explosionStore.bornExplosion({
                    x: nv.x + TANK.size / 2,
                    y: nv.y + TANK.size / 2,
                    staff: 'tank'
                  })
                  pv.score += ENEMY.explosionScore
                  enemyStore.enemyDestroyed(nv.id)
                }
                break
              case 'watch':
                for (const [_, nv] of enemyStore.enemies) {
                  enemyStore.updateEnemy({
                    id: nv.id,
                    moving: false,
                    canMove: false,
                    canFire: false,
                  })
                }
                const o_watch = {
                  type: v.type,
                  prog: 0,
                }
                this.effect.set(v.type, o_watch)
                break
              case 'helmet':
                protectionStore.bornProtection(pv.role, 'helmet')
                break
              case 'shovel':
                const mapStore = useMapStore()
                const o_shovel = {
                  type: v.type,
                  steelBase: true,
                  dur: 0,
                  prog: 0,
                  isBrick: false,
                }
                this.effect.set(v.type, o_shovel)
                mapStore.changeBase(2)
              case 'pistol':
                pv.power = Math.min(pv.power + 3, BUTTLET.level.length - 1)
                break
            }
            skip = true
            this.prop.delete(v.id)
            break
          }
        }
        if (skip) continue

        if (v.dur >= PROP.dur) {
          skip = true
          this.prop.delete(v.id)
        }
        if (skip) continue

        const periodTime = PROP.dur - (PROP.flicker.interval * (PROP.flicker.count - 1))
        if (v.dur >= periodTime) {
          const item = this.prop.get(v.id)
          if (v.prog >= PROP.flicker.interval) {
            this.prop.set(v.id, { ...item, visible: !item.visible, prog: 0, dur: item.dur + ft })
          } else {
            this.prop.set(v.id, { ...item, prog: v.prog + ft, dur: item.dur + ft })
          }
        }
      }
    },
    playEffect(ft) {
      for (const [_, v] of this.effect) {
        switch (v.type) {
          case 'watch':
            const enemyStore = useEnemyStore()
            if (v.prog >= PROP.watch.effectDur) {
              for (const [_, nv] of enemyStore.enemies) {
                enemyStore.updateEnemy({
                  id: nv.id,
                  moving: true,
                  canMove: true,
                  canFire: true,
                })
              }
              this.effect.delete(v.type)
            } else {
              v.prog += ft
            }
            break
          case 'shovel':
            const mapStore = useMapStore()
            const ef = PROP.shovel.effect
            const mid = ef.dur - ef.flicker.interval * ef.flicker.count
            if (v.dur >= ef.dur) {
              mapStore.changeBase(1)
              this.effect.delete(v.type)
            } {
              v.dur += ft
              if (v.dur >= mid) {
                if (v.prog >= ef.flicker.interval) {
                  v.prog = 0
                  mapStore.changeBase(v.isBrick ? 2 : 1)
                  v.isBrick = !v.isBrick
                } else {
                  v.prog += ft
                }
              }
            }
            break
        }
      }
    }
  }
})